package com.google.code.twiddling.core.bootstrap;

import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.code.twiddling.core.chain.ChainManager;
import com.google.code.twiddling.core.command.AbstractCommand;
import com.google.code.twiddling.core.command.CommandContext;
import com.google.code.twiddling.core.command.CommandMetadata;
import com.google.code.twiddling.core.command.DefaultCommandContext;
import com.google.code.twiddling.core.command.ICommand;
import com.google.code.twiddling.core.context.Environment;
import com.google.code.twiddling.core.context.PersistenceableEnvironment;
import com.google.code.twiddling.core.io.BasicIOManager;
import com.google.code.twiddling.core.io.CommandListener;
import com.google.code.twiddling.core.io.IOManager;
import com.google.code.twiddling.core.io.Render;
import com.google.code.twiddling.core.io.jline.JLineIOManager;
import com.google.code.twiddling.core.log.LogUtils;
import com.google.code.twiddling.core.registry.AnnotationCommandRegistry;
import com.google.code.twiddling.core.registry.CommandNotFoundException;
import com.google.code.twiddling.core.registry.CommandRegistry;
import com.google.code.twiddling.core.util.StringUtil;


/**
 * 
 * Initialize a Console with:
 * 1. Default Environment.
 * 2. Default IO
 * 3. Command Registry
 * 4. ChainManager, who is taking charge of processing the command line.
 * 
 * @author <a href="mailto:howard.gao@gmail.com">Howard Gao</a> 
 * @author <a href="mailto:jeff.yuchang@gmail.com">Jeff Yu</a>
 * 
 */

public class TwiddlingShell implements CommandListener{
	
	public static final String BANNER = "\n" + 
"    OOOOOOO           OO          O       O    O       OO\n" +
"       O             O  O         O       O    O      O  O\n" +
"       O             O  O         O       O    O      O  O\n" +
"       O   O    O     OO      OO  O   OOO O    O       OO      OO      OOO\n" +
"       O   O     O           O  OOO  O   OO    O             OO  OO   O   OO\n" +
"       O   O     O     O    O     O O     O    O        O    O     O O     O\n" +
"       O   O  O  O     O    O     O O     O    O        O    O     O O     O\n" +
"       O   O  O  O     O    O     O O     O    O        O    O     O O     O\n" +
"       O   O  OO O     O     O  OOO  O   OO    O        O    O     O  O   OO\n" +
"       O    OO  O      O      OO  O   OOO O     OO      O    O     O   OOO O\n" +
"                                                                           O\n" +
"                                                                      OOOOO\n\n" ;
	
	public static final String VERSION = " Version 1.0 M1 by Twiddling Team. \n";
	public static final String HOMEPAGE = " http://code.google.com/p/twiddling \n\n";
	
	private boolean isExit;
	private IOManager ioManager;
	private CommandRegistry registry;
	private Environment environment;
	private TwiddlingShutDownHook shutDownHook;

    private ChainManager chainManager = ChainManager.getInstance();
    
    private static final Logger logger = LogUtils.getL7dLogger(TwiddlingShell.class);
    

	public TwiddlingShell() throws Exception{
		environment = new PersistenceableEnvironment(this);
		ioManager = new BasicIOManager(environment);
		ioManager.registerCommandListener(this);
		registry = new AnnotationCommandRegistry();
	}

	public static void main(String[] args) throws Exception{
		TwiddlingShell shell = new TwiddlingShell();
		//
		System.setProperty(Render.SUPPORT_ANSI, "false");
		
		shell.start();
		System.exit(0);
	}

	private void start() {
		environment.activate();
		ioManager.start();
		shutDownHook = new TwiddlingShutDownHook();
		Runtime.getRuntime().addShutdownHook(shutDownHook);
		synchronized (this) {
			while (!isExit) {
				try {
					wait();
				} catch (InterruptedException e) {
					LogUtils.log(logger, Level.WARNING, e.getMessage(), e);
				}
			}
		}
		
		ioManager.println("Twiddling Shell exited.");
		
		ioManager.stop();
		
	}

	public synchronized void stop(boolean fromHandler) {
		if (!fromHandler) {
			Runtime.getRuntime().removeShutdownHook(shutDownHook);
		}
		ioManager.println("Shutting down twiddling ...");
		isExit = true;		
		synchronized (this) {
			notify();
		}
	}

	public synchronized void stop() {
		stop(false);
	}
	
	
	public void process(String cmdString){		
		if (StringUtil.isEmpty(cmdString)) {
			return ;
		}
		
		CommandMetadata data = new CommandMetadata(cmdString);
		CommandContext context = new DefaultCommandContext(environment, ioManager.getIO(), registry, data);
		try {
			chainManager.processChain(context);
		} catch (Exception e) {
			if (e instanceof CommandNotFoundException) {
				ioManager.getIO().getOut().println(e.getMessage());
				ioManager.getIO().getOut().println("Try ' help ' form more information." );
			} else {			
				ioManager.getIO().getOut().print(data.getName() + " : ");
				ioManager.getIO().getOut().println(e.getMessage());
				ioManager.getIO().getOut().println("Try '" + data.getName() + " -h ' for more information.");
				
				ICommand command = context.getCommand();
				if (command != null && command instanceof AbstractCommand) {
					AbstractCommand theCommand = (AbstractCommand)command;				
					if (theCommand.isVerbose()) {
						e.printStackTrace();
					}
				}
			}
			
			LogUtils.log(logger, Level.SEVERE, e.getMessage(), e);
		}
	}

	public class TwiddlingShutDownHook extends Thread {
		public void run() {
			TwiddlingShell.this.stop(true);
		}
	}
}
